home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1996 #15 / Monster Media Number 15 (Monster Media)(July 1996).ISO / utils / mt100.zip / MT.CPP < prev    next >
C/C++ Source or Header  |  1996-06-04  |  18KB  |  496 lines

  1. // Move To drive/directory.
  2. // Copyright 1996 Jason Hood
  3. // Started:  14 April, 1996.
  4. // Finished:  4 June.
  5.  
  6. // Will change drive as well as directory.
  7. // Allows use of slash ("/") as well as backslash ("\").
  8. // Can use multiple dots (eg. treats "..." as "../..").
  9. // Can select previous directory by using "mt;" (or "mt ;" if not so lazy).
  10. // Can also select the directory before the previous directory by using ";;".
  11. // Partial directory names, where searches always start from the root.
  12. // "mt @drives" will construct a directory structure file for drives. "mt @"
  13. // will update the directory structure file for drives already in the file.
  14. // The path is specified via mtmem. The filename is "mtdirs.dat".
  15. // Path and previous directories are stored in memory, allocated by mtmem.
  16.  
  17. // Acknowledgements: Tim Jones' WASTED.PAS for finding directories.
  18.  
  19. // You are free to use this code, or a portion thereof, as long as an
  20. // appropriate acknowledgement is made.
  21.  
  22. // Questions, suggestions and comments to hoodj@topaz.cqu.edu.au.
  23.  
  24.  
  25. #include <dir.h>
  26. #include <string.h>
  27. #include <iostream.h>
  28. #include <iomanip.h>
  29. #include <fstream.h>
  30. #include <ctype.h>
  31. #include <stdlib.h>
  32.  
  33.  
  34. #define version "1.00"
  35.  
  36. char olddir[MAXDIR], newdir[MAXDIR];    // The current and new directories
  37.  
  38. char mtdirs[MAXPATH],            // Directory structure file
  39.      prev[2][MAXDIR];            // Previous directories
  40.  
  41. char **dirs;                // Directories for each drive
  42. int dirnum;                // Number of directories on each drive
  43. const int MaxDirs = 1500;        // Maximum number of directories/drive
  44.  
  45. int find(const char* path);               // Try and find path
  46. int partdir(int num, char* partial[], char drive); // Find a partial directory
  47. void restore(char drv, const char* rev);       // Make a proper pathname
  48.  
  49. int dirfile(char* drives);        // Create directory structure for drives
  50. void finddirs(const char* startdir, const char* parent = "");// Find directories
  51. char* finddirec(struct ffblk &dir, const char* name = "");   // Find dir. name
  52. long index(ofstream& os, char drv);    // Index and write drv's directories
  53. int sort(const void* a, const void* b);    // How the directories are sorted
  54. int subs(const char* path);        // Number of directories in path
  55.  
  56. void help();                // Display help screen
  57.  
  58. inline ofstream& wlong(ofstream& os, long num) {  // Write a long to a file
  59.   os.write((char*)&num, sizeof(long));            // as a binary value
  60.   return os;
  61. }
  62.  
  63. inline ifstream& rlong(ifstream& is, long& num) { // Read a long from a file
  64.   is.read((char*)&num, sizeof(long));              // as a binary value
  65.   return is;
  66. }
  67.  
  68.  
  69. void main(int argc, char* argv[]) {
  70.  
  71.   int bprev = 1,            // Update both previous directories
  72.       result;                // Result from functions
  73.   char drive,                // Drive specified
  74.        *env;                // Environment string
  75.   unsigned mtmem = 0;            // Segment assigned by mtmem.com
  76.  
  77.   if (!(env = getenv("MTMEM")) || *env == '#') {
  78.     cout << "You must run \"MTMEM\" first." << endl;
  79.     return;
  80.   }
  81.   // Convert the string into a segment address, assuming it to be valid
  82.   for (; *env; env++) mtmem = (mtmem << 4) | (*env & 0x0f);
  83.   // It is easier to do this than to muck about with far pointers
  84.   movedata(mtmem, 0, _DS, (unsigned)mtdirs, MAXPATH+MAXDIR*2);
  85.  
  86.   olddir[0] = getdisk() + 'A';        // Retrieve the current directory
  87.   olddir[1] = ':';
  88.   olddir[2] = '\\';
  89.   getcurdir(0, olddir+3);
  90.  
  91.   if (argc == 1) {                      // No parameters
  92.     cout << endl
  93.      << "        Current directory = " << olddir << endl
  94.      << "       Previous directory = " << prev[0] << endl
  95.      << "Before previous directory = " << prev[1] << endl;
  96.     return;
  97.   }
  98.  
  99.   char *&dir = argv[1],            // Alias the first argument
  100.        &what = dir[0];            // and its first character
  101.  
  102.   if (what == '?' || dir[1] == '?') {    // First or second character help
  103.     help();
  104.     return;
  105.   }
  106.  
  107.   if (what == '@') {            // (Re)construct directory structure
  108.     result = dirfile(dir+1);
  109.     if (result) cout << "Unable to " << (result == 1 ? "create" : "open")
  110.              << " \"" << mtdirs << "\".";
  111.     return;
  112.   }
  113.  
  114.   if (what == ';') {            // Use a previous directory.
  115.     bprev = (dir[1] == ';');        // 0 - previous, 1 - before previous
  116.     strcpy(newdir, prev[bprev]);
  117.   }
  118.   else {
  119.     if (dir[1] == ':') {        // If there is a drive
  120.       newdir[0] = drive = toupper(what);
  121.       newdir[1] = ':';            // then make the new directory
  122.       newdir[2] = 0;            // start with it
  123.       dir += 2;                // and skip past it
  124.     }
  125.     else newdir[0] = drive = 0;
  126.  
  127.     if (what == '.') {            // Parent shortcut
  128.       for (int dots = 0; dir[dots] == '.'; dots++); // Count dots
  129.       if (dots > 2)            // If more than two then replace
  130.     for (int j = 0; j < dots-2; j++) { // each dot after two with "../"
  131.       strcat(newdir, "../");    // eg. replace first dot of "..." with
  132.       dir++;            // "../" to get "../.."
  133.     }
  134.     }
  135.     if (argc > 2 || !find(dir))    {    // Two parameters or an unfound subdir
  136.       result = partdir(argc-1, argv+1, drive);      // requires a search
  137.       if (result) {            // It hasn't worked
  138.     switch (result) {
  139.       case 1: cout << "Unable to open \"" << mtdirs << "\"."; break;
  140.       case 2: cout << drive << ": has not been scanned."; break;
  141.       case 3: cout << "No match found.";
  142.     }
  143.     cout << endl;
  144.     return;
  145.       }
  146.     }
  147.   }
  148.  
  149.   if (chdir(newdir) == -1) {
  150.     cout << (newdir[1] == ':' ? "Invalid drive or d" : "D")
  151.      << "irectory does not exist." << endl;
  152.     return;
  153.   }
  154.   if (newdir[1] == ':' && newdir[0] != olddir[0])
  155.     setdisk(toupper(newdir[0]) - 'A');
  156.  
  157.   // Is finding string length more efficient than copying the whole array?
  158.   movedata(_DS, (unsigned)olddir, mtmem, MAXPATH, strlen(olddir)+1);
  159.   if (bprev && strcmp(prev[0], olddir))
  160.     movedata(_DS, (unsigned)prev[0], mtmem, MAXPATH+MAXDIR, strlen(prev[0])+1);
  161. }
  162.  
  163.  
  164. // See if newdir+path exists (newdir may contain the drive). If path ends in
  165. // "*" it will select the first directory that starts with path.
  166. // Return 0 if a search for path is required, otherwise 1.
  167.  
  168. int find(const char* path) {
  169.  
  170.   int star = (path[strlen(path)-1] == '*');    // Is there a star?
  171.   struct ffblk dir;
  172.  
  173.   if (*path == 0) {            // Only a drive has been specified
  174.     newdir[2] = '.';            // So chdir will work
  175.     newdir[3] = 0;
  176.     return 1;
  177.   }
  178.  
  179.   strcat(newdir, path);
  180.   if (!finddirec(dir, newdir) &&    // The path doesn't exist,
  181.       *path != '.' && !star &&        // isn't a shortcut, doesn't end in star
  182.       !strchr(path, '/') && !strchr(path, '\\')) // and is a one name path
  183.     return 0;                // then need to search for it
  184.  
  185.   if (star) {                // Expand the name found
  186.     for (int j = strlen(newdir)-2;    // Find where the name starts
  187.        j >= 0 &&            // The very beginning or
  188.        newdir[j] != '/' &&        // after a path or
  189.        newdir[j] != '\\' &&
  190.        newdir[j] != ':';        // after the drive
  191.      j--);
  192.     strcpy(newdir+j+1, dir.ff_name);    // Replace with the actual name
  193.   }
  194.   return 1;                // Found, or not searching
  195. }
  196.  
  197.  
  198. // From the partial names try and find a match. If drive is null then search
  199. // all drives in the file. If a match is found return 0, otherwise return 1
  200. // for unable to open the file; 2 for unscanned drive; 3 for no match.
  201.  
  202. int partdir(int num, char* partial[], char drive) {
  203.  
  204.   ifstream mt(mtdirs, ios::in | ios::binary);
  205.   if (!mt) return 1;            // Bit of a problem
  206.  
  207.   char drv, let,            // Drive we're on, first letter to match
  208.        path[MAXDIR],            // A possible path
  209.        *dir;                // Subdirectory to match
  210.   long index, next = 0;            // File positions
  211.   int found;                // Pretty much self-explanatory
  212.  
  213.   for (int j = 0; j < num; j++) strupr(partial[j]);
  214.  
  215.   do {                    // For each drive required to search
  216.     mt.seekg(next);            // Point to the drive
  217.     mt.get(drv);            // Get this drive letter
  218.     rlong(mt, next);            // Pointer to next drive
  219.     if (drive) {            // Find the drive we want
  220.       while (next && drv != drive) {
  221.     mt.seekg(next);
  222.     mt.get(drv);
  223.     rlong(mt, next);
  224.       }
  225.       if (drv != drive) return 2;    // Drive not in file
  226.     }
  227.     if (isalpha(let = *partial[num-1])) {  // First name start with a letter?
  228.       mt.seekg((let-'A') * sizeof(long), ios::cur);
  229.       rlong(mt, index);            // Then find the appropriate position
  230.       if (index == 0) continue;        // No directories start with this letter
  231.       mt.seekg(index);            // Starting position
  232.     }
  233.     else mt.seekg(26 * sizeof(long), ios::cur);    // Start straight after indices
  234.  
  235.     mt.getline(path, MAXDIR);
  236.     while (let == *path) {
  237.       found = 0;            // Number of matches found
  238.       dir = path;            // First subdirectory
  239.       for (int j = num-1; j >= 0 &&
  240.               !strncmp(partial[j], dir, strlen(partial[j])); j--) {
  241.     found++;
  242.     dir = strchr(dir, '/');        // Find the next subdirectory
  243.     if (!dir) break;         // The path has reached the root
  244.     dir++;                // Point past the slash
  245.       }
  246.       if (found == num) {        // A successful match
  247.     restore(drv, path);        // Put the full path into newdir
  248.     if (strcmp(newdir, olddir)) {    // Only successful if not already here
  249.       next = 0;            // To terminate the do loop
  250.       break;            // To terminate the while loop
  251.     }
  252.       }
  253.       mt.getline(path, MAXDIR);         // Try another match
  254.     }
  255.   } while (!drive && next);        // Drive not specified and more exist
  256.  
  257.   return (found == num ? 0 : 3);
  258. }
  259.  
  260.  
  261. // Take a drive and reversed pathname separated by slashes and create newdir.
  262. // Use memcpy instead of strncat since it is faster (should be, anyway).
  263. void restore(char drv, const char* rev) {
  264.  
  265.   char *beg = newdir + 3;        // Where to place the current subdir.
  266.   int end = strlen(rev) - 1;        // Where it ends
  267.  
  268.   newdir[0] = drv;            // The drive where the match was found
  269.   newdir[1] = ':';
  270.   newdir[2] = '\\';            // Backslashes for comparison purposes
  271.  
  272.   for (int j = end-1; j > 0; j--) {    // Search backwards for slashes
  273.     if (rev[j] == '/') {        // Found one, so from here to the
  274.       memcpy(beg, rev+j+1, end-j);    // previous match is the subdir.
  275.       beg += end-j;
  276.       *(beg++) = '\\';            // Add the separator
  277.       end = --j;            // Ready for the next
  278.     }
  279.   }
  280.   memcpy(beg, rev, ++end);        // Final subdirectory
  281.   *(beg+end) = 0;            // Very important not to forget this
  282. }
  283.  
  284.  
  285. // Create the directory structure (see finddirs, index and sort for details).
  286. // If drives is an empty string then scan the drives already in the file,
  287. // otherwise scan those drives specified (assume they're valid).
  288. // Return 0 for all okay; 1 for unable to create file; 2 for unable to open it.
  289.  
  290. int dirfile(char* drives) {
  291.  
  292.   int cur = getdisk(),            // Current drive
  293.       drv[26],                // Drive numbers (A: = 0)
  294.       n = strlen(strupr(drives)),    // Number of drives to scan
  295.       j, k;                // Loop variables
  296.   long last;                // Last drive position
  297.  
  298.   if (n) {                // Specified drives
  299.     for (j = 0; j < n; j++) drv[j] = drives[j] - 'A';
  300.     cout << "Creating";
  301.   }
  302.   else {                // Drives already there
  303.     ifstream mtin(mtdirs, ios::in | ios::binary);
  304.     if (!mtin) return 2;        // Probably not yet been created
  305.     do {
  306.       drv[n++] = mtin.get() - 'A';
  307.       rlong(mtin, last);
  308.       mtin.seekg(last);
  309.     } while (last);
  310.     mtin.close();
  311.     cout << "Updating";
  312.   }
  313.   cout << " \"" << mtdirs << "\"." << endl;
  314.  
  315.   ofstream mt(mtdirs, ios::out | ios::binary);
  316.   if (!mt) return 1;            // Uh oh!
  317.  
  318.   dirs = new char*[MaxDirs];        // Create the array of directories
  319.  
  320.   for (j = 0; j < n; j++) {
  321.     setdisk(drv[j]);            // Make the drive active
  322.     cout << "Drive " << char(drv[j]+'A') << " -> Directories =    0";
  323.     dirnum = 0;
  324.     finddirs("/");            // Get all the directories
  325.  
  326.     cout << " -> Sorting";
  327.     qsort((void*)dirs, dirnum, sizeof(dirs[0]), sort);
  328.  
  329.     cout << " -> Indexing";
  330.     last = index(mt, drv[j]+'A');
  331.  
  332.     for (k = 0; k < dirnum; k++)    // Free the memory for the next drive
  333.       delete []dirs[k];
  334.     cout << " -> Done." << endl;
  335.   }
  336.   mt.seekp(last);            // Write zero for last drive
  337.   wlong(mt, 0);
  338.   mt.close();
  339.   delete []dirs;
  340.  
  341.   setdisk(cur);                // Restore current drive
  342.   chdir(olddir);            // and directory
  343.   return 0;
  344. }
  345.  
  346.  
  347. // Starting from startdir recursively find all directories for the current
  348. // drive and store them as a reverse path separated by slashes, ending in a
  349. // newline. eg: "\language\bc" will be stored as "bc/language<\n>".
  350. // Global variables dirs holds the directories; dirnum has the number found.
  351. // The initial starting directory has no parent.
  352. // Use memcpy since I need lengths anyway.
  353.  
  354. void finddirs(const char* startdir, const char* parent) {
  355.  
  356.   struct ffblk dir;
  357.   int nlen;                // Length of current subdirectory
  358.   static int plen = 0;            // Length of startdir's parent
  359.   static char *name;            // The name of the subdirectory
  360.  
  361.   chdir(startdir);
  362.   name = finddirec(dir, "*.*");
  363.   while (name) {
  364.     // Make some space: length of this dir., slash, length of parent, null
  365.     dirs[dirnum] = new char[(nlen = strlen(name)) + plen + 2];
  366.     memcpy(dirs[dirnum], name, nlen);        // Copy this directory
  367.     dirs[dirnum][nlen++] = '/';            // Add the slash
  368.     memcpy(dirs[dirnum]+nlen, parent, plen+1);    // Add the parent (and null)
  369.     plen += nlen;                // The new parent length
  370.     dirs[dirnum][plen-1] = '\n';        // Last slash becomes newline
  371.     cout << "\b\b\b\b" << setw(4) << ++dirnum;    // Update and output count
  372.     finddirs(name, dirs[dirnum-1]);          // Get any subdirectories
  373.     chdir("..");                // Back to this one
  374.     plen -= nlen;                // The old parent length
  375.     name = finddirec(dir);            // The next directory
  376.   }
  377. }
  378.  
  379. // Find a directory that matches name, ignoring the . and .. directories.
  380. // If name is not given, continue the find.
  381. // Return the name found, or NULL.
  382.  
  383. char* finddirec(struct ffblk &dir, const char* name) {
  384.  
  385.   const int FA_DIREC = 0x10;        // From dos.h
  386.   int found;
  387.  
  388.   if (*name) found = findfirst(name, &dir, FA_DIREC);
  389.   else       found = findnext(&dir);
  390.  
  391.   while (found == 0 && (dir.ff_attrib != FA_DIREC || *dir.ff_name == '.'))
  392.     found = findnext(&dir);
  393.  
  394.   return (found == 0 ? dir.ff_name : NULL);
  395. }
  396.  
  397.  
  398. // Write and index the directories for drive. First write the drive letter and
  399. // an index to the next drive. Following this are 26 indices for each letter of
  400. // the alphabet. An index of zero means no directories start with that letter.
  401. // Then the directories, one per line, and a blank line to finish.
  402. // It returns the position of the next drive index so zero can be written to
  403. // indicate no more drives. However, os is left at the end of the file.
  404.  
  405. long index(ofstream& os, char drive) {
  406.  
  407.   char let = 'A';            // Current index letter
  408.   long index, lin, din;            // File positions
  409.  
  410.   os.put(drive);            // Write the drive letter
  411.   din = os.tellp();            // The next drive index position
  412.   wlong(os, 0);                // Where the next drive will start
  413.   lin = os.tellp();            // The current letter index position
  414.   for (int j = 0; j < 26; j++) wlong(os, 0);    // Letters' index positions
  415.  
  416.   for (j = 0; j < dirnum; j++) {
  417.     while (*dirs[j] > let) {        // In case there are no directories
  418.       let++;                            // that start with let
  419.       lin += sizeof(long);
  420.     }
  421.     if (*dirs[j] == let) {        // Write the index for this letter
  422.       index = os.tellp();        // This is where it begins
  423.       os.seekp(lin);            // Go to letter index position
  424.       wlong(os, index);            // Write the value
  425.       os.seekp(0, ios::end);        // Back to the end
  426.       let++;                // Next letter
  427.       lin += sizeof(long);        // and its index position
  428.     }
  429.     os << dirs[j];            // Write the directory
  430.   }
  431.   os << endl;                // No more directories
  432.   index = os.tellp();                   // Store the position for next drive
  433.   os.seekp(din);
  434.   wlong(os, index);
  435.   os.seekp(0, ios::end);        // Ready for next drive
  436.   return din;                // For writing 0 for no more drives
  437. }
  438.  
  439.  
  440. // This determines how the directories will be searched. Smaller paths are
  441. // placed before larger paths, non-alphabetical entries are before alphabetical
  442. // directories, and the directories are sorted alphabetically.
  443.  
  444. int sort(const void *a, const void *b) {
  445.  
  446.   char *dir1 = *(char**)a,              // qsort thinks I am sorting an array
  447.        *dir2 = *(char**)b;        // of pointers - this gets the string
  448.  
  449.   int let1 = isalpha(*dir1), len1,    // First directory is alphabetic
  450.       let2 = isalpha(*dir2), len2;    // Second directory is alphabetic
  451.  
  452.   if (*dir1 == *dir2) {            // Both start with the same character
  453.     len1 = subs(dir1);            // so sort by path length
  454.     len2 = subs(dir2);
  455.     if (len1 < len2) return -1;         // Same size path length will be
  456.     else if (len1 > len2) return 1;    // sorted by name below
  457.   }
  458.   if ((let1 && let2) || (!let1 && !let2)) // Both are/are not letters
  459.     return strcmp(dir1, dir2);        // so sort by name
  460.  
  461.   return (let1 ? 1 : -1);        // Letters placed after non-letters
  462. }
  463.  
  464. // Determine the number of directories in a path for the sort routine.
  465. int subs(const char* path) {
  466.   int n = 0;                               // Number of slashes
  467.   for (; *path; path++) if (*path == '/') n++;
  468.   return n+1;                // Number of paths
  469. }
  470.  
  471.  
  472. // Display the help screen.
  473. void help() {
  474.   cout <<                // One really long string
  475.   "\n"
  476.   "mt - Move To drive/directory. mtmem - resident memory for mt.\n"
  477.   "Copyright 1996 Jason Hood. Freeware. Version "version".\n"
  478.   "\n"
  479.   "mt         Display current, previous and before previous directories.\n"
  480.   "mt @dc     Create directory structure for drives D: and C:.\n"
  481.   "mt @       Update directory structure (rescan drives).\n"
  482.   "mt ...     Equivalent to \"mt ../..\".\n"
  483.   "mt;        Move back to previous directory.\n"
  484.   "mt;;       Move back to directory before previous directory.\n"
  485.   "mt wg*     Move to first subdirectory starting with \"wg\".\n"
  486.   "           (Only the ending directory can end in \"*\".)\n"
  487.   "mt w/g     Move to subdirectory \"w\" subsubdirectory \"g\".\n"
  488.   "mt wg      Move to subdirectory \"wg\". If that fails\n"
  489.   "           search for a directory that starts with \"wg\".\n"
  490.   "mt w g     Search for a directory starting with \"w\" that\n"
  491.   "           has a subdirectory starting with \"g\".\n"
  492.   "\n"
  493.   "Searches always begin from the root directory.\n"
  494.   "\n";
  495. }
  496.